#!/usr/bin/env python3
"""
Changelog 自动更新工具

这个脚本可以：
1. 基于 Git 提交历史自动生成 changelog 条目
2. 解析提交消息中的变更类型
3. 自动更新版本号
4. 生成符合 Keep a Changelog 格式的输出
"""

import re
import subprocess
import argparse
from datetime import datetime
from typing import List, Dict, Tuple, Optional
from pathlib import Path
import json


class ChangelogGenerator:
    """Changelog 生成器"""
    
    def __init__(self, changelog_path: str = "CHANGELOG.md"):
        self.changelog_path = Path(changelog_path)
        self.change_types = {
            'feat': 'Added',
            'add': 'Added', 
            'fix': 'Fixed',
            'bug': 'Fixed',
            'docs': 'Changed',
            'style': 'Changed',
            'refactor': 'Changed',
            'perf': 'Changed',
            'test': 'Changed',
            'build': 'Changed',
            'ci': 'Changed',
            'chore': 'Changed',
            'revert': 'Removed',
            'remove': 'Removed',
            'delete': 'Removed',
            'break': 'Changed',  # Breaking changes
            'security': 'Security',
            'deprecate': 'Deprecated'
        }
    
    def run_git_command(self, cmd: List[str]) -> str:
        """执行 git 命令并返回输出"""
        try:
            result = subprocess.run(
                cmd, 
                capture_output=True, 
                text=True, 
                check=True,
                cwd=self.changelog_path.parent,
                encoding='utf-8',
                errors='ignore'
            )
            return result.stdout.strip() if result.stdout else ""
        except subprocess.CalledProcessError as e:
            print(f"Git 命令执行失败: {e}")
            return ""
    
    def get_commits_since_tag(self, since_tag: Optional[str] = None) -> List[Dict]:
        """获取自指定标签以来的提交"""
        if since_tag:
            cmd = ['git', 'log', f'{since_tag}..HEAD', '--pretty=format:%H|%s|%ai|%an']
        else:
            cmd = ['git', 'log', '--pretty=format:%H|%s|%ai|%an', '-n', '20']
        
        output = self.run_git_command(cmd)
        commits = []
        
        for line in output.split('\n'):
            if line.strip():
                parts = line.split('|', 3)
                if len(parts) >= 4:
                    commits.append({
                        'hash': parts[0],
                        'message': parts[1],
                        'date': parts[2],
                        'author': parts[3]
                    })
        
        return commits
    
    def parse_commit_message(self, message: str) -> Tuple[str, str, str]:
        """解析提交消息，提取类型、范围和描述"""
        # 匹配 conventional commits 格式: type(scope): description
        pattern = r'^(\w+)(?:\(([^)]+)\))?\s*:\s*(.+)$'
        match = re.match(pattern, message)
        
        if match:
            commit_type = match.group(1).lower()
            scope = match.group(2) or ''
            description = match.group(3)
            return commit_type, scope, description
        
        # 如果不匹配格式，尝试从消息开头提取关键词
        message_lower = message.lower()
        for keyword, change_type in self.change_types.items():
            if message_lower.startswith(keyword):
                return keyword, '', message
        
        # 默认归类为 Changed
        return 'change', '', message
    
    def categorize_commits(self, commits: List[Dict]) -> Dict[str, List[Dict]]:
        """将提交按变更类型分类"""
        categorized = {
            'Added': [],
            'Changed': [], 
            'Deprecated': [],
            'Removed': [],
            'Fixed': [],
            'Security': []
        }
        
        for commit in commits:
            commit_type, scope, description = self.parse_commit_message(commit['message'])
            category = self.change_types.get(commit_type, 'Changed')
            
            commit['parsed_type'] = commit_type
            commit['scope'] = scope
            commit['description'] = description
            commit['category'] = category
            
            categorized[category].append(commit)
        
        return categorized
    
    def get_latest_version(self) -> str:
        """从 changelog 或 git tags 获取最新版本"""
        # 尝试从 git tags 获取
        cmd = ['git', 'tag', '--sort=-version:refname']
        output = self.run_git_command(cmd)
        
        if output:
            tags = output.split('\n')
            for tag in tags:
                if re.match(r'^v?\d+\.\d+\.\d+', tag):
                    return tag.lstrip('v')
        
        # 从 changelog 解析
        if self.changelog_path.exists():
            content = self.changelog_path.read_text(encoding='utf-8')
            version_pattern = r'## \[(\d+\.\d+\.\d+)\]'
            match = re.search(version_pattern, content)
            if match:
                return match.group(1)
        
        return '0.0.0'
    
    def increment_version(self, version: str, increment_type: str = 'patch') -> str:
        """递增版本号"""
        parts = version.split('.')
        major, minor, patch = int(parts[0]), int(parts[1]), int(parts[2])
        
        if increment_type == 'major':
            major += 1
            minor = 0
            patch = 0
        elif increment_type == 'minor':
            minor += 1
            patch = 0
        else:  # patch
            patch += 1
        
        return f"{major}.{minor}.{patch}"
    
    def generate_changelog_section(self, version: str, categorized_commits: Dict[str, List[Dict]]) -> str:
        """生成 changelog 部分内容"""
        date_str = datetime.now().strftime('%Y-%m-%d')
        section = f"\n## [{version}] - {date_str}\n\n"
        
        for category, commits in categorized_commits.items():
            if commits:
                section += f"### {category}\n\n"
                for commit in commits:
                    scope_str = f"**{commit['scope']}**: " if commit['scope'] else ""
                    section += f"- {scope_str}{commit['description']}\n"
                section += "\n"
        
        return section
    
    def update_changelog(self, new_version: str, categorized_commits: Dict[str, List[Dict]]):
        """更新 changelog 文件"""
        new_section = self.generate_changelog_section(new_version, categorized_commits)
        
        if not self.changelog_path.exists():
            # 如果文件不存在，创建基本结构
            content = self.create_initial_changelog()
        else:
            content = self.changelog_path.read_text(encoding='utf-8')
        
        # 在 [Unreleased] 部分后插入新版本
        unreleased_pattern = r'(## \[Unreleased\].*?(?=\n## \[|\n---|\Z))'
        match = re.search(unreleased_pattern, content, re.DOTALL)
        
        if match:
            # 找到插入位置
            insert_pos = match.end()
            content = content[:insert_pos] + new_section + content[insert_pos:]
        else:
            # 如果没找到 Unreleased 部分，在文件开头添加
            content = content + new_section
        
        # 更新链接部分
        content = self.update_version_links(content, new_version)
        
        # 清空 Unreleased 部分
        content = self.clear_unreleased_section(content)
        
        self.changelog_path.write_text(content, encoding='utf-8')
        print(f"✅ Changelog 已更新到版本 {new_version}")
    
    def create_initial_changelog(self) -> str:
        """创建初始 changelog 结构"""
        return """# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

### Changed

### Deprecated

### Removed

### Fixed

### Security

---

"""
    
    def clear_unreleased_section(self, content: str) -> str:
        """清空 Unreleased 部分"""
        pattern = r'(## \[Unreleased\]\n\n)(.*?)(\n---|\n## \[)'
        replacement = r'\1### Added\n\n### Changed\n\n### Deprecated\n\n### Removed\n\n### Fixed\n\n### Security\n\n\3'
        return re.sub(pattern, replacement, content, flags=re.DOTALL)
    
    def update_version_links(self, content: str, new_version: str) -> str:
        """更新版本链接"""
        # 这里可以根据你的项目 URL 自定义
        repo_url = "https://github.com/yourusername/todo-web"
        
        # 添加新版本链接
        link_pattern = r'(\[Unreleased\]: .+?\.\.\.HEAD\n)'
        new_link = f"[{new_version}]: {repo_url}/releases/tag/v{new_version}\n"
        
        if re.search(link_pattern, content):
            content = re.sub(link_pattern, f"\\1{new_link}", content)
        
        return content


def main():
    parser = argparse.ArgumentParser(description='自动生成和更新 Changelog')
    parser.add_argument('--version-type', choices=['major', 'minor', 'patch'], 
                       default='patch', help='版本递增类型')
    parser.add_argument('--since-tag', help='从指定标签开始获取提交')
    parser.add_argument('--dry-run', action='store_true', help='预览模式，不实际修改文件')
    parser.add_argument('--changelog-path', default='CHANGELOG.md', help='Changelog 文件路径')
    
    args = parser.parse_args()
    
    generator = ChangelogGenerator(args.changelog_path)
    
    print("🔍 获取提交历史...")
    commits = generator.get_commits_since_tag(args.since_tag)
    
    if not commits:
        print("❌ 没有找到新的提交")
        return
    
    print(f"📝 找到 {len(commits)} 个提交")
    
    # 分类提交
    categorized = generator.categorize_commits(commits)
    
    # 显示提交分类
    for category, commits_in_category in categorized.items():
        if commits_in_category:
            print(f"\n{category}:")
            for commit in commits_in_category:
                print(f"  - {commit['description']} ({commit['hash'][:8]})")
    
    # 计算新版本号
    current_version = generator.get_latest_version()
    new_version = generator.increment_version(current_version, args.version_type)
    
    print(f"\n📋 版本更新: {current_version} → {new_version}")
    
    if args.dry_run:
        print("\n🔍 预览模式 - 生成的 changelog 内容:")
        print(generator.generate_changelog_section(new_version, categorized))
    else:
        # 更新 changelog
        generator.update_changelog(new_version, categorized)
        
        # 可选：创建 git tag
        create_tag = input(f"\n🏷️  是否创建 git tag v{new_version}? (y/N): ")
        if create_tag.lower() == 'y':
            subprocess.run(['git', 'tag', f'v{new_version}'])
            print(f"✅ 已创建标签 v{new_version}")


if __name__ == '__main__':
    main()